<?php
// @formatter:off
/**
 * @file WCSessions.php
 * @author Alejandro Dario Simi
 * @date $Date: 2013-05-13 04:08:49 +0000 (Mon, 13 May 2013) $
 *
 * $Id: WCSessions.php 64 2013-05-13 04:08:49Z daemonraco@gmail.com $
 * $URL: http://wcomix.googlecode.com/svn/tags/wcomix-1.0-BETA2/includes/security/WCSessions.php $
 */
// @formatter:on

require_once "{$wcPaths["includes-directory"]}/security/WCSession.php";
require_once "{$wcPaths["includes-directory"]}/security/WCSessionKeep.php";

if(!session_id()) {
	session_start();
}

/**
 * @class WCSessions
 */
class WCSessions {
	protected static $_Instance;

	/**
	 * @var WCConnection
	 */
	protected $_db = null;
	/**
	 * @var string
	 */
	protected $_dbprefix = "";
	/**
	 * @var string
	 */
	protected $_id = "";
	/**
	 * @var string
	 */
	protected $_ip = "";
	/**
	 * @var boolean
	 */
	protected $_isLogged = false;
	/**
	 * @var WCUser
	 */
	protected $_user = null;
	/**
	 * @var int
	 */
	protected $_userId = false;
	/**
	 * @var WCSession
	 */
	protected $_session = null;
	/**
	 * @var WCSessionKeep
	 */
	protected $_sessionKeep = null;

	/**
	 * Class constructor
	 */
	protected function __construct() {
		global $wcDatabaseAccess;

		$this->_id = session_id();

		$this->clearVariables();

		$this->_ip = isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : "unknown";
		$this->_db = WCConnection::Instance();
		$this->_dbprefix = $wcDatabaseAccess['prefix'];
		$this->_session = new WCSession();
		$this->_sessionKeep = new WCSessionKeep();

		$this->clearSessions();
		$this->checkSession();
	}
	/**
	 * Prevent users to clone the instance.
	 */
	public function __clone() {
		trigger_error(__CLASS__.": Clone is not allowed.", E_USER_ERROR);
	}
	/*
	 * Public methods.
	 */
	public function id() {
		return $this->_id;
	}
	public function ip() {
		return $this->_ip;
	}
	public function isLogged() {
		return $this->_isLogged;
	}
	/**
	 * This method tries to log in the current sension into database.
	 * @param $id Identifier of user to be logged in.
	 * @return Returns true when the user was successfully logged in.
	 */
	public function login($id) {
		if(!$this->isLogged()) {
			$stmt = null;
			$stmtId = __CLASS__."::".__FUNCTION__;
			if($this->_db->has($stmtId)) {
				$stmt = $this->_db->get($stmtId);
			} else {
				$query = "insert\n";
				$query .= "        into {$this->_dbprefix}sessions (sess_id, sess_user_id, sess_ip)\n";
				$query .= "        values (:session, :user, :ip)";

				$stmt = $this->_db->prepare($stmtId, $query);
			}

			$params = array(
				":session" => $this->_id,
				":user" => $id,
				":ip" => $this->_ip
			);
			$stmt->execute($params);

			return $this->checkSession();
		} else {
			return true;
		}
	}
	/**
	 * Removes current session id from database.
	 * @return Returns true when session was successfully removed.
	 */
	public function logout() {
		$stmt = null;
		$stmtId = __CLASS__."::".__FUNCTION__."[sessions]";
		if($this->_db->has($stmtId)) {
			$stmt = $this->_db->get($stmtId);
		} else {
			$query = "delete from     {$this->_dbprefix}sessions\n";
			$query .= "where           sess_id = :session";

			$stmt = $this->_db->prepare($stmtId, $query);
		}

		$params = array(":session" => $this->_id);
		$stmt->execute($params);

		$stmt = null;
		$stmtId = __CLASS__."::".__FUNCTION__."[keeps]";
		if($this->_db->has($stmtId)) {
			$stmt = $this->_db->get($stmtId);
		} else {
			$query = "delete from     {$this->_dbprefix}session_keeps\n";
			$query .= "where           skp_user = :user\n";
			$query .= " and            skp_code = :code\n";
			$query .= " and            skp_salt = :salt\n";
			$query .= " and            skp_ip   = :ip\n";

			$stmt = $this->_db->prepare($stmtId, $query);
		}

		$params = array(
			":user" => $this->userId(),
			":code" => WCSessionKeep::CodeGenertor($this->_session->salt),
			":salt" => $this->_session->salt,
			":ip" => $this->_ip
		);
		$stmt->execute($params);

		return !$this->checkSession();
	}
	public function page() {
		return $_SERVER['REQUEST_URI'];
	}
	public function setRememberMe() {
		if($this->isLogged()) {
			$salt = $this->_session->salt;
			$code = WCSessionKeep::CodeGenertor($salt);

			$stmt = null;
			$stmtId = __CLASS__."::".__FUNCTION__;
			if($this->_db->has($stmtId)) {
				$stmt = $this->_db->get($stmtId);
			} else {
				$query = "insert\n";
				$query .= "        into {$this->_dbprefix}session_keeps (skp_user, skp_code, skp_salt, skp_ip, skp_timestamp)\n";
				$query .= "        values (:user, :code, :salt, :ip, now())";

				$stmt = $this->_db->prepare($stmtId, $query);
			}

			$params = array(
				":user" => $this->userId(),
				":code" => $code,
				":salt" => $salt,
				":ip" => $this->_ip
			);
			if($stmt->execute($params)) {
				$this->_sessionKeep->load($this->_db->lastInsertId());
				$this->_sessionKeep->updateCookies();
			}
		}
	}
	public function user() {
		return $this->_user;
	}
	public function userId() {
		return $this->_userId;
	}
	// Protected methods
	/**
	 * This method checks if current session id is registered in database.
	 * When it is already registered, it updates that entry, mainly to keep
	 * session aging field up to date.
	 * @return Return true when it is already registered. In other words, it
	 * sets $this->_isLogged.
	 */
	protected function checkSession() {
		$this->_isLogged = false;

		@$this->_session->load($this->_id);
		if($this->_session->ok() && $this->_session->ip == $this->_ip) {
			global $wcUsersHolder;

			$user = $wcUsersHolder->item($this->_session->user_id);
			if($user && $user->ok()) {
				$this->_isLogged = true;
				$this->_userId = $user->id();

				$this->_user = $user;

			}
			$this->_session->page = $this->page();
		} elseif(!$this->_session->ok()) {
			static $attempt = true;

			if($attempt && $this->_sessionKeep->loadByCookies()) {
				$attempt = false;
				$this->login($this->_sessionKeep->user);
				$this->_sessionKeep->updateCookies();
				$this->_session->salt = $this->_sessionKeep->salt;
			}
			$attempt = true;
		}

		if(!$this->isLogged()) {
			$user = $this->_sessionKeep->lastUser();
			if($user) {
				global $wcThemeAssigns;

				$wcThemeAssigns["LAST-USER-ID"] = $user->id();
				$wcThemeAssigns["LAST-USER-USERNAME"] = $user->username();
			}
		}

		return $this->isLogged();
	}
	/**
	 * This method removes expired sessions.
	 */
	protected function clearSessions() {
		global $wcDefaults;

		$stmt = null;
		$stmtId = __CLASS__."::".__FUNCTION__;
		if($this->_db->has($stmtId)) {
			$stmt = $this->_db->get($stmtId);
		} else {
			$query = "delete from {$this->_dbprefix}sessions\n";
			$query .= "where       (now() - sess_timestamp) > :maxtime";
			$stmt = $this->_db->prepare($stmtId, $query, true);
		}

		$stmt->execute(array(":maxtime" => $wcDefaults['session-max-time']));
	}
	/**
	 * This method clears every instance property that might indicate it is logged in.
	 */
	protected function clearVariables() {
		$this->_isLogged = false;
		$this->_userId = false;
		if($this->_user) {
			unset($this->_user);
		}
		$this->_user = null;
	}
	/*
	 * Public class methods
	 */
	public static function Instance() {
		if(!isset(self::$_Instance)) {
			$c = __CLASS__;
			self::$_Instance = new $c;
		}

		return self::$_Instance;
	}
};
?>
